﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

namespace NFox.Runtime.Dll
{
    public class Library : IDisposable
    {
        [DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)]
        private static extern bool FreeLibrary(IntPtr hModule);

        [DllImport("kernel32")]
        private static extern IntPtr LoadLibrary(string lpFileName);

        [DllImport("kernel32")]
        private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

        public string Name;
        internal ModuleBuilder ModuleBuilder;
        private IntPtr _ip;

        private Dictionary<string, Function> _funcs =
            new Dictionary<string, Function>();

        private static int _tmpIndex;

        private string TmpModuleName
        {
            get { return string.Format("{0}.{1}", Name, _tmpIndex++); }
        }

        public CallingConvention CallingConv;

        internal Library(string name, CallingConvention callingConv)
        {
            Name = Path.GetFileNameWithoutExtension(name);
            CallingConv = callingConv;
            _ip = LoadLibrary(name);
            ModuleBuilder =
                InvokeApi
                .GetAssemblyBuilder()
                .DefineDynamicModule(TmpModuleName);
        }

        /// <summary>
        /// 生成当前模块中的函数
        /// </summary>
        internal void CreateFunctions()
        {
            ModuleBuilder.CreateGlobalFunctions();
            foreach (var mi in ModuleBuilder.GetMethods())
                _funcs[mi.Name].MethodInfo = mi;
            ModuleBuilder =
                InvokeApi
                .GetAssemblyBuilder()
                .DefineDynamicModule(TmpModuleName);
        }

        internal bool OK
        {
            get { return _ip != IntPtr.Zero; }
        }

        public Function this[string name]
        {
            get
            {
                if (!ContainsKey(name))
                    Regist(name);
                if (ContainsKey(name))
                    return _funcs[name];
                return null;
            }
        }

        public bool ContainsKey(string key)
        {
            return _funcs.ContainsKey(key);
        }

        /// <summary>
        /// 注册函数，并按别名尾字符分配CharSet模式
        /// </summary>
        /// <param name="alias">别名(函数入口点)</param>
        /// <returns></returns>
        public bool Regist(string alias)
        {
            return Regist(alias, alias);
        }

        /// <summary>
        /// 注册函数，并按别名尾字符分配CharSet模式
        /// </summary>
        /// <param name="alias">别名</param>
        /// <param name="entryPoint">函数入口点</param>
        /// <returns>是否注册成功</returns>
        public bool Regist(string alias, string entryPoint)
        {
            CharSet charSet =
                alias[alias.Length - 1] == 'W' ?
                CharSet.Unicode :
                CharSet.Ansi;
            if (!ContainsKey(alias))
            {
                var ip = GetProcAddress(_ip, entryPoint);
                if (ip == IntPtr.Zero)
                    return false;
                else
                    _funcs.Add(alias, new Function(this, alias, ip, entryPoint, charSet));
            }
            return true;
        }

        /// <summary>
        /// 注册函数，并按别名尾字符分配CharSet模式
        /// </summary>
        /// <param name="funcNames">字典(别名,函数入口点)</param>
        public void Regist(Dictionary<string, string> funcNames)
        {
            foreach (var kv in funcNames)
                Regist(kv.Key, kv.Value);
        }

        public void Dispose()
        {
            FreeLibrary(_ip);
        }
    }
}